<!DOCTYPE html>
<html lang="zh-cn">
<head>
   
    <link type="text/css" rel="stylesheet" href="/bundles/blog-common.css?v=KOZafwuaDasEedEenI5aTy8aXH0epbm6VUJ0v3vsT_Q1"/>
<link id="MainCss" type="text/css" rel="stylesheet" href="/skins/ThinkInside/bundle-ThinkInside.css?v=RRjf6pEarGnbXZ86qxNycPfQivwSKWRa4heYLB15rVE1"/>
<link type="text/css" rel="stylesheet" href="/blog/customcss/428549.css?v=%2fam3bBTkW5NBWhBE%2fD0lcyJv5UM%3d"/>

</head>
<body>
<a name="top"></a>

<div id="page_begin_html"></div><script>load_page_begin_html();</script>

<div id="topics">
	<div class = "post">
		<h1 class = "postTitle">
			<a id="cb_post_title_url" class="postTitle2" href="https://www.cnblogs.com/frankdeng/p/9301653.html">Spark（三）RDD与广播变量、累加器</a>
		</h1>
		<div class="clear"></div>
		<div class="postBody">
			<div id="cnblogs_post_body" class="blogpost-body"><h2>一、RDD的概述</h2>
<h3>1.1 什么是RDD&nbsp;</h3>
<p>RDD（Resilient Distributed Dataset）叫做<strong>弹性分布式数据集</strong>，<strong>是Spark中最基本的数据抽象</strong>，它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD具有数据流模型的特点：自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中，后续的查询能够重用工作集，这极大地提升了查询速度。</p>
<h3>1.2 RDD的属性</h3>
<p>（1）一组分片（Partition），即数据集的基本组成单位。对于RDD来说，每个分片都会被一个计算任务处理，并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数，如果没有指定，那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。</p>
<p>（2）一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的，每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合，不需要保存每次计算的结果。</p>
<p>（3）RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD，所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时，Spark可以通过这个依赖关系重新计算丢失的分区数据，而不是对RDD的所有分区进行重新计算。</p>
<p>（4）一个Partitioner，即RDD的分片函数。当前Spark中实现了两种类型的分片函数，一个是基于哈希的HashPartitioner，另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD，才会有Partitioner，非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量，也决定了parent RDD Shuffle输出时的分片数量。</p>
<p>（5）一个列表，存储存取每个Partition的优先位置（preferred location）。对于一个HDFS文件来说，这个列表保存的就是每个Partition所在的块的位置。按照&ldquo;移动数据不如移动计算&rdquo;的理念，Spark在进行任务调度的时候，会尽可能地将计算任务分配到其所要处理数据块的存储位置。</p>
<h3>1.3 WordCount粗图解RDD</h3>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421133911520-1150689001.png" alt="" width="1200" /></p>
<p>其中hello.txt</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421134031551-1670646166.png" alt="" /></p>
<h2>二、RDD的创建方式</h2>
<h3>2.1 读取文件生成</h3>
<p>可以从Hadoop支持的任何存储源创建分布式数据集，包括本地文件系统，HDFS，Cassandra，HBase等</p>
<div class="cnblogs_code">
<pre>scala&gt; val file = sc.textFile(<span style="color: #800000;">"</span><span style="color: #800000;">/spark/hello.txt</span><span style="color: #800000;">"</span><span style="color: #000000;">)</span></pre>
</div>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421134515478-653027491.png" alt="" width="1024" /></p>
<h3>2.2 并行化方式创建<span lang="EN-US">RDD</span></h3>
<p>由一个已经存在的Scala集合创建。</p>
<div class="cnblogs_code">
<pre>scala&gt; val array = Array(<span style="color: #800080;">1</span>,<span style="color: #800080;">2</span>,<span style="color: #800080;">3</span>,<span style="color: #800080;">4</span>,<span style="color: #800080;">5</span><span style="color: #000000;">)
array: Array[Int] </span>= Array(<span style="color: #800080;">1</span>, <span style="color: #800080;">2</span>, <span style="color: #800080;">3</span>, <span style="color: #800080;">4</span>, <span style="color: #800080;">5</span><span style="color: #000000;">)

scala</span>&gt; val rdd =<span style="color: #000000;"> sc.parallelize(array)
rdd: org.apache.spark.rdd.RDD[Int] </span>= ParallelCollectionRDD[<span style="color: #800080;">27</span>] at parallelize at &lt;console&gt;:<span style="color: #800080;">26</span><span style="color: #000000;">

scala</span>&gt; </pre>
</div>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421134820158-111255712.png" alt="" width="1024" /></p>
<h3>2.3 其他方式</h3>
<p><span lang="EN-US">读取数据库等等其他的操作。也可以生成<span lang="EN-US">RDD。</span></span></p>
<p><span lang="EN-US">RDD可以通过其他的<span lang="EN-US">RDD转换而来的。</span></span></p>
<h2>三、RDD编程API</h2>
<p>Spark支持两个类型（算子）操作：Transformation和Action</p>
<h3>3.1 Transformation</h3>
<p>主要做的是就是将一个已有的RDD生成另外一个RDD。Transformation具有<strong>lazy</strong><strong>特性(延迟加载)</strong>。Transformation算子的代码不会真正被执行。只有当我们的程序里面遇到一个action算子的时候，代码才会真正的被执行。这种设计让Spark更加有效率地运行。</p>
<p><strong>常用的Transformation</strong>：</p>
<table style="height: 2069px; width: 942px;" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="261">
<p align="center"><strong>转换</strong></p>
</td>
<td valign="top" width="307">
<p align="center"><strong>含义</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>map</strong>(func)</p>
</td>
<td valign="top" width="307">
<p>返回一个新的RDD，该RDD由每一个输入元素经过func函数转换后组成</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>filter</strong>(func)</p>
</td>
<td valign="top" width="307">
<p>返回一个新的RDD，该RDD由经过func函数计算后返回值为true的输入元素组成</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>flatMap</strong>(func)</p>
</td>
<td valign="top" width="307">
<p>类似于map，但是每一个输入元素可以被映射为0或多个输出元素（所以func应该返回一个序列，而不是单一元素）</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>mapPartitions</strong>(func)</p>
</td>
<td valign="top" width="307">
<p>类似于map，但独立地在RDD的每一个分片上运行，因此在类型为T的RDD上运行时，func的函数类型必须是Iterator[T] =&gt; Iterator[U]</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>mapPartitionsWithIndex</strong>(func)</p>
</td>
<td valign="top" width="307">
<p>类似于mapPartitions，但func带有一个整数参数表示分片的索引值，因此在类型为T的RDD上运行时，func的函数类型必须是</p>
<p>(Int, Interator[T]) =&gt; Iterator[U]</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>sample</strong>(withReplacement, fraction, seed)</p>
</td>
<td valign="top" width="307">
<p>根据fraction指定的比例对数据进行采样，可以选择是否使用随机数进行替换，seed用于指定随机数生成器种子</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>union</strong>(otherDataset)</p>
</td>
<td valign="top" width="307">
<p>对源RDD和参数RDD求并集后返回一个新的RDD</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>intersection</strong>(otherDataset)</p>
</td>
<td valign="top" width="307">
<p>对源RDD和参数RDD求交集后返回一个新的RDD（交集）</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>distinct</strong>([numTasks]))</p>
</td>
<td valign="top" width="307">
<p>对源RDD进行去重后返回一个新的RDD</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>groupByKey</strong>([numTasks])</p>
</td>
<td valign="top" width="307">
<p>在一个(K,V)的RDD上调用，返回一个(K, Iterator[V])的RDD</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>reduceByKey</strong>(func, [numTasks])</p>
</td>
<td valign="top" width="307">
<p>在一个(K,V)的RDD上调用，返回一个(K,V)的RDD，使用指定的reduce函数，将相同key的值聚合到一起，与groupByKey类似，reduce任务的个数可以通过第二个可选的参数来设置</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>aggregateByKey</strong>(zeroValue)(seqOp, combOp, [numTasks])</p>
</td>
<td valign="top" width="307">
<p>先按分区聚合 再总的聚合&nbsp;&nbsp; 每次要跟初始值交流 例如：aggregateByKey(0)(_+_,_+_) 对k/y的RDD进行操作</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>sortByKey</strong>([ascending], [numTasks])</p>
</td>
<td valign="top" width="307">
<p>在一个(K,V)的RDD上调用，K必须实现Ordered接口，返回一个按照key进行排序的(K,V)的RDD</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>sortBy</strong>(func,[ascending], [numTasks])</p>
</td>
<td valign="top" width="307">
<p>与sortByKey类似，但是更灵活 第一个参数是根据什么排序&nbsp; 第二个是怎么排序 false倒序&nbsp;&nbsp; 第三个排序后分区数&nbsp; 默认与原RDD一样</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>join</strong>(otherDataset, [numTasks])</p>
</td>
<td valign="top" width="307">
<p>在类型为(K,V)和(K,W)的RDD上调用，返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD&nbsp; 相当于内连接（求交集）</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>cogroup</strong>(otherDataset, [numTasks])</p>
</td>
<td valign="top" width="307">
<p>在类型为(K,V)和(K,W)的RDD上调用，返回一个(K,(Iterable&lt;V&gt;,Iterable&lt;W&gt;))类型的RDD</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>cartesian</strong>(otherDataset)</p>
</td>
<td valign="top" width="307">
<p>两个RDD的笛卡尔积&nbsp; 的成很多个K/V</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>pipe</strong>(command, [envVars])</p>
</td>
<td valign="top" width="307">
<p>调用外部程序</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>coalesce</strong>(numPartitions<strong>)&nbsp;&nbsp;&nbsp; </strong></p>
</td>
<td valign="top" width="307">
<p>重新分区 第一个参数是要分多少区，第二个参数是否shuffle 默认false&nbsp; 少分区变多分区 true&nbsp;&nbsp; 多分区变少分区 false</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>repartition</strong>(numPartitions)</p>
</td>
<td valign="top" width="307">
<p>重新分区 必须shuffle&nbsp; 参数是要分多少区&nbsp; 少变多</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>repartitionAndSortWithinPartitions</strong>(partitioner)</p>
</td>
<td valign="top" width="307">
<p>重新分区+排序&nbsp; 比先分区再排序效率高&nbsp; 对K/V的RDD进行操作</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>foldByKey</strong>(zeroValue)(seqOp)</p>
</td>
<td valign="top" width="307">
<p>该函数用于K/V做折叠，合并处理 ，与aggregate类似&nbsp;&nbsp; 第一个括号的参数应用于每个V值&nbsp; 第二括号函数是聚合例如：_+_</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>combineByKey</strong></p>
</td>
<td valign="top" width="307">
<p>合并相同的key的值 rdd1.combineByKey(x =&gt; x, (a: Int, b: Int) =&gt; a + b, (m: Int, n: Int) =&gt; m + n)</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>partitionBy</strong><strong>（partitioner）</strong></p>
</td>
<td valign="top" width="307">
<p>对RDD进行分区&nbsp; partitioner是分区器 例如new HashPartition(2</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>cache</strong></p>
</td>
<td rowspan="2" valign="top" width="307">
<p>RDD缓存，可以避免重复计算从而减少时间，区别：cache内部调用了persist算子，cache默认就一个缓存级别MEMORY-ONLY ，而persist则可以选择缓存级别</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>persist</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>Subtract</strong><strong>（rdd）</strong></p>
</td>
<td valign="top" width="307">
<p>返回前rdd元素不在后rdd的rdd（差集）</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>leftOuterJoin</strong></p>
</td>
<td valign="top" width="307">
<p>leftOuterJoin类似于SQL中的左外关联left outer join，返回结果以前面的RDD为主，关联不上的记录为空。只能用于两个RDD之间的关联，如果要多个RDD关联，多关联几次即可。</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>rightOuterJoin</strong></p>
</td>
<td valign="top" width="307">
<p>rightOuterJoin类似于SQL中的有外关联right outer join，返回结果以参数中的RDD为主，关联不上的记录为空。只能用于两个RDD之间的关联，如果要多个RDD关联，多关联几次即可</p>
</td>
</tr>
<tr>
<td valign="top" width="261">
<p><strong>subtractByKey</strong></p>
</td>
<td valign="top" width="307">
<p>substractByKey和基本转换操作中的subtract类似只不过这里是针对K的，返回在主RDD中出现，并且不在otherRDD中出现的元素</p>
</td>
</tr>
</tbody>
</table>
<h3>3.2 Action</h3>
<p>触发代码的运行，我们一段spark代码里面至少需要有一个action操作。</p>
<p><strong>常用的Action</strong>:</p>
<table style="height: 1061px; width: 930px;" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="260">
<p align="center"><strong>动作</strong></p>
</td>
<td valign="top" width="308">
<p align="center"><strong>含义</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>reduce</strong>(<em>func</em>)</p>
</td>
<td valign="top" width="308">
<p>通过func函数聚集RDD中的所有元素，这个功能必须是课交换且可并联的</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>collect</strong>()</p>
</td>
<td valign="top" width="308">
<p>在驱动程序中，以数组的形式返回数据集的所有元素</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>count</strong>()</p>
</td>
<td valign="top" width="308">
<p>返回RDD的元素个数</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>first</strong>()</p>
</td>
<td valign="top" width="308">
<p>返回RDD的第一个元素（类似于take(1)）</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>take</strong>(<em>n</em>)</p>
</td>
<td valign="top" width="308">
<p>返回一个由数据集的前n个元素组成的数组</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>takeSample</strong>(<em>withReplacement</em>,<em>num</em>, [<em>seed</em>])</p>
</td>
<td valign="top" width="308">
<p>返回一个数组，该数组由从数据集中随机采样的num个元素组成，可以选择是否用随机数替换不足的部分，seed用于指定随机数生成器种子</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>takeOrdered</strong>(<em>n</em>,&nbsp;<em>[ordering]</em>)</p>
</td>
<td valign="top" width="308">
<p><span><span>使用自然顺序或自定义比较器</span><span>返回</span><span>RDD&nbsp;</span><span>的前</span></span><em><span><span>n个</span></span></em><span><span>元素。</span></span></p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>saveAsTextFile</strong>(<em>path</em>)</p>
</td>
<td valign="top" width="308">
<p>将数据集的元素以textfile的形式保存到HDFS文件系统或者其他支持的文件系统，对于每个元素，Spark将会调用toString方法，将它装换为文件中的文本</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>saveAsSequenceFile</strong>(<em>path</em>)&nbsp;</p>
</td>
<td valign="top" width="308">
<p>将数据集中的元素以Hadoop sequencefile的格式保存到指定的目录下，可以使HDFS或者其他Hadoop支持的文件系统。</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>saveAsObjectFile</strong>(<em>path</em>)&nbsp;</p>
</td>
<td valign="top" width="308">
<p><span><span>使用Java序列化以简单格式编写数据集的元素，然后可以使用Java序列化加载</span></span><code>SparkContext.objectFile()</code><span><span>。</span></span>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>countByKey</strong>()</p>
</td>
<td valign="top" width="308">
<p>针对(K,V)类型的RDD，返回一个(K,Int)的map，表示每一个key对应的元素个数。</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>foreach</strong>(<em>func</em>)</p>
</td>
<td valign="top" width="308">
<p>在数据集的每一个元素上，运行函数func进行更新。</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>aggregate</strong></p>
</td>
<td valign="top" width="308">
<p>先对分区进行操作，在总体操作</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>reduceByKeyLocally</strong></p>
</td>
<td valign="top" width="308">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>lookup</strong></p>
</td>
<td valign="top" width="308">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>top</strong></p>
</td>
<td valign="top" width="308">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>fold</strong></p>
</td>
<td valign="top" width="308">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="260">
<p><strong>foreachPartition</strong></p>
</td>
<td valign="top" width="308">
<p>&nbsp;</p>
</td>
</tr>
</tbody>
</table>
<h3>3.3 WordCount代码编写</h3>
<p>使用maven进行项目构建</p>
<h4>&nbsp;（1）使用scala进行编写</h4>
<p>查看官方网站，需要导入2个依赖包</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421144526496-1152731884.png" alt="" width="1200" /></p>
<p>详细代码</p>
<p>SparkWordCountWithScala.scala</p>
<div class="cnblogs_code">
<pre>import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object SparkWordCountWithScala {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf()
    /**
      * 如果这个参数不设置，默认认为你运行的是集群模式
      * 如果设置成local代表运行的是local模式
      */
    conf.setMaster("local")
    //设置任务名
    conf.setAppName("WordCount")
    //创建SparkCore的程序入口
    val sc = new SparkContext(conf)
    //读取文件 生成RDD
    val file: RDD[String] = sc.textFile("E:\\hello.txt")
    //把每一行数据按照，分割
    val word: RDD[String] = file.flatMap(_.split(","))
    //让每一个单词都出现一次
    val wordOne: RDD[(String, Int)] = word.map((_,1))
    //单词计数
    val wordCount: RDD[(String, Int)] = wordOne.reduceByKey(_+_)
    //按照单词出现的次数 降序排序
    val sortRdd: RDD[(String, Int)] = wordCount.sortBy(tuple =&gt; tuple._2,false)
    //将最终的结果进行保存
    sortRdd.saveAsTextFile("E:\\result")
    sc.stop()
  }</pre>
</div>
<p>运行结果</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421151823241-1753369845.png" alt="" /></p>
<h4>（2）使用java jdk7进行编写</h4>
<p>SparkWordCountWithJava7.java</p>
<div class="cnblogs_code">
<pre>import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import scala.Tuple2;
import java.util.Arrays;
import java.util.Iterator;

public class SparkWordCountWithJava7 {
    public static void main(String[] args) {<br />
        SparkConf conf = new SparkConf();
        conf.setMaster("local");
        conf.setAppName("WordCount");
        JavaSparkContext sc = new JavaSparkContext(conf);
        JavaRDD&lt;String&gt; fileRdd = sc.textFile("E:\\hello.txt");

        JavaRDD&lt;String&gt; wordRDD = fileRdd.flatMap(new FlatMapFunction&lt;String, String&gt;() {
            @Override
            public Iterator&lt;String&gt; call(String line) throws Exception {
                return Arrays.asList(line.split(",")).iterator();
            }
        });

        JavaPairRDD&lt;String, Integer&gt; wordOneRDD = wordRDD.mapToPair(new PairFunction&lt;String, String, Integer&gt;() {
            @Override
            public Tuple2&lt;String, Integer&gt; call(String word) throws Exception {
                return new Tuple2&lt;&gt;(word, 1);
            }
        });

        JavaPairRDD&lt;String, Integer&gt; wordCountRDD = wordOneRDD.reduceByKey(new Function2&lt;Integer, Integer, Integer&gt;() {
            @Override
            public Integer call(Integer i1, Integer i2) throws Exception {
                return i1 + i2;
            }
        });

        JavaPairRDD&lt;Integer, String&gt; count2WordRDD = wordCountRDD.mapToPair(new PairFunction&lt;Tuple2&lt;String, Integer&gt;, Integer, String&gt;() {
            @Override
            public Tuple2&lt;Integer, String&gt; call(Tuple2&lt;String, Integer&gt; tuple) throws Exception {
                return new Tuple2&lt;&gt;(tuple._2, tuple._1);
            }
        });

        JavaPairRDD&lt;Integer, String&gt; sortRDD = count2WordRDD.sortByKey(false);

        JavaPairRDD&lt;String, Integer&gt; resultRDD = sortRDD.mapToPair(new PairFunction&lt;Tuple2&lt;Integer, String&gt;, String, Integer&gt;() {
            @Override
            public Tuple2&lt;String, Integer&gt; call(Tuple2&lt;Integer, String&gt; tuple) throws Exception {
                return new Tuple2&lt;&gt;(tuple._2, tuple._1);
            }
        });

        resultRDD.saveAsTextFile("E:\\result7");

    }
}</pre>
</div>
<h4>&nbsp;（3）使用java jdk8进行编写</h4>
<p>lambda表达式</p>
<p>SparkWordCountWithJava8.java</p>
<div class="cnblogs_code">
<pre>import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import scala.Tuple2;
import java.util.Arrays;

public class SparkWordCountWithJava8 {
    public static void main(String[] args) {<br />
        SparkConf conf = new SparkConf();
        conf.setAppName("WortCount");
        conf.setMaster("local");
        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaRDD&lt;String&gt; fileRDD = sc.textFile("E:\\hello.txt");
        JavaRDD&lt;String&gt; wordRdd = fileRDD.flatMap(line -&gt; Arrays.asList(line.split(",")).iterator());
        JavaPairRDD&lt;String, Integer&gt; wordOneRDD = wordRdd.mapToPair(word -&gt; new Tuple2&lt;&gt;(word, 1));
        JavaPairRDD&lt;String, Integer&gt; wordCountRDD = wordOneRDD.reduceByKey((x, y) -&gt; x + y);
        JavaPairRDD&lt;Integer, String&gt; count2WordRDD = wordCountRDD.mapToPair(tuple -&gt; new Tuple2&lt;&gt;(tuple._2, tuple._1));
        JavaPairRDD&lt;Integer, String&gt; sortRDD = count2WordRDD.sortByKey(false);
        JavaPairRDD&lt;String, Integer&gt; resultRDD = sortRDD.mapToPair(tuple -&gt; new Tuple2&lt;&gt;(tuple._2, tuple._1));
        resultRDD.saveAsTextFile("E:\\result8");

    }</pre>
</div>
<h3>3.4 WordCount执行过程图</h3>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180425153140543-8294264.png" alt="" width="1200" /></p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180425153515149-1269337605.png" alt="" width="1200" /></p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180425153556469-238789142.png" alt="" /></p>
<h2>四、RDD的宽依赖和窄依赖</h2>
<h3>4.1&nbsp;<strong>RDD依赖关系的本质内幕</strong></h3>
<p>由于RDD是粗粒度的操作数据集，每个Transformation操作都会生成一个新的RDD，所以RDD之间就会形成类似流水线的前后依赖关系；RDD和它依赖的父RDD（s）的关系有两种不同的类型，即窄依赖（narrow dependency）和宽依赖（wide dependency）。如图所示显示了RDD之间的依赖关系。</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180425151022105-1121308065.png" alt="" /></p>
<p align="left">从图中可知：</p>
<p align="left"><strong>窄依赖：</strong>是指每个父RDD的一个Partition最多被子RDD的一个Partition所使用，例如map、filter、union等操作都会产生窄依赖；（独生子女）</p>
<p align="left"><strong>宽依赖：</strong>是指一个父RDD的Partition会被多个子RDD的Partition所使用，例如groupByKey、reduceByKey、sortByKey等操作都会产生宽依赖；（超生）</p>
<p align="left">需要特别说明的是对join操作有两种情况：</p>
<p align="left">（1）图中左半部分join：如果两个RDD在进行join操作时，一个RDD的partition仅仅和另一个RDD中已知个数的Partition进行join，那么这种类型的join操作就是窄依赖，例如图1中左半部分的join操作(join with inputs co-partitioned)；</p>
<p align="left">（2）图中右半部分join：其它情况的join操作就是宽依赖,例如图1中右半部分的join操作(join with inputs not co-partitioned)，由于是需要父RDD的所有partition进行join的转换，这就涉及到了shuffle，因此这种类型的join操作也是宽依赖。</p>
<p align="left">总结：</p>
<blockquote>
<p align="left">在这里我们是从父RDD的partition被使用的个数来定义窄依赖和宽依赖，因此可以用一句话概括下：如果父RDD的一个Partition被子RDD的一个Partition所使用就是窄依赖，否则的话就是宽依赖。因为是确定的partition数量的依赖关系，所以RDD之间的依赖关系就是窄依赖；由此我们可以得出一个推论：即窄依赖不仅包含一对一的窄依赖，还包含一对固定个数的窄依赖。</p>
<p align="left">一对固定个数的窄依赖的理解：即子RDD的partition对父RDD依赖的Partition的数量不会随着RDD数据规模的改变而改变；换句话说，无论是有100T的数据量还是1P的数据量，在窄依赖中，子RDD所依赖的父RDD的partition的个数是确定的，而宽依赖是shuffle级别的，数据量越大，那么子RDD所依赖的父RDD的个数就越多，从而子RDD所依赖的父RDD的partition的个数也会变得越来越多。</p>
</blockquote>
<h3 align="left">4.2&nbsp;<strong>依赖关系下的数据流视图</strong></h3>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180425151747136-185909749.png" alt="" /></p>
<p align="left">在spark中，会根据RDD之间的依赖关系将DAG图（有向无环图）划分为不同的阶段，对于窄依赖，由于partition依赖关系的确定性，partition的转换处理就可以在同一个线程里完成，窄依赖就被spark划分到同一个stage中，而对于宽依赖，只能等父RDD shuffle处理完成后，下一个stage才能开始接下来的计算。</p>
<p align="left"><strong>因此spark划分stage的整体思路是</strong>：从后往前推，遇到宽依赖就断开，划分为一个stage；遇到窄依赖就将这个RDD加入该stage中。因此在图2中RDD C,RDD D,RDD E,RDDF被构建在一个stage中,RDD A被构建在一个单独的Stage中,而RDD B和RDD G又被构建在同一个stage中。</p>
<p align="left">在spark中，Task的类型分为2种：<strong>ShuffleMapTask</strong>和<strong>ResultTask</strong>；</p>
<p align="left">简单来说，DAG的最后一个阶段会为每个结果的partition生成一个ResultTask，即每个Stage里面的Task的数量是由该Stage中最后一个RDD的Partition的数量所决定的！而其余所有阶段都会生成ShuffleMapTask；之所以称之为ShuffleMapTask是因为它需要将自己的计算结果通过shuffle到下一个stage中；也就是说上图中的stage1和stage2相当于mapreduce中的Mapper,而ResultTask所代表的stage3就相当于mapreduce中的reducer。</p>
<p align="left">在之前动手操作了一个wordcount程序，因此可知，Hadoop中MapReduce操作中的Mapper和Reducer在spark中的基本等量算子是map和reduceByKey;不过区别在于：Hadoop中的MapReduce天生就是排序的；而reduceByKey只是根据Key进行reduce，但spark除了这两个算子还有其他的算子；因此从这个意义上来说，Spark比Hadoop的计算算子更为丰富。</p>
<h2>五、广播变量与累加器</h2>
<p>在spark程序中，当一个传递给Spark操作(例如map和reduce)的函数在远程节点上面运行时，Spark操作实际上操作的是这个函数所用变量的一个独立副本。这些变量会被复制到每台机器上，并且这些变量在远程机器上的所有更新都不会传递回驱动程序。通常跨任务的读写变量是低效的，但是，Spark还是为两种常见的使用模式提供了两种有限的共享变量：广播变量（broadcast variable）和累加器（accumulator）</p>
<h2>六、广播变量broadcast variable</h2>
<h3>6.1 广播变量的意义</h3>
<p>如果我们要在分布式计算里面分发大对象，例如：字典，集合，黑白名单等，这个都会由Driver端进行分发，一般来讲，如果这个变量不是广播变量，那么每个task就会分发一份，这在<strong>task数目十分多的情况下Driver的带宽会成为系统的瓶颈，而且会大量消耗task服务器上的资源</strong>，如果将这个变量声明为广播变量，那么只是每个executor拥有一份，这个executor启动的task会共享这个变量，节省了通信的成本和服务器的资源。</p>
<h3>6.2 广播变量图解</h3>
<p>错误的，不使用广播变量</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421162057226-1988253385.png" alt="" /></p>
<p>正确的，使用广播变量的情况</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421162148572-1992224700.png" alt="" /></p>
<h3>6.3 如何定义一个广播变量</h3>
<div class="cnblogs_code">
<pre>val a = 3
val broadcast = sc.broadcast(a)</pre>
</div>
<h3>6.4如何还原一个广播变量</h3>
<div class="cnblogs_code">
<pre>val c = broadcast.value</pre>
</div>
<h3 class="15"><strong>6.5 广播变量使用</strong></h3>
<div class="cnblogs_code">
<pre>val conf = new<span> SparkConf()
conf.setMaster("local").setAppName("brocast"<span>)
val sc = new<span> SparkContext(conf)
val list = List("hello hadoop"<span>)
val broadCast =<span> sc.broadcast(list)
val lineRDD = sc.textFile("./words.txt"<span>)
lineRDD.filter { x =&gt; broadCast.value.contains(x) }.foreach<span> { println}
sc.stop()</span></span></span></span></span></span></span></pre>
</div>
<h3>6.6 定义广播变量注意点&nbsp;</h3>
<p>变量一旦被定义为一个广播变量，那么这个变量只能读，不能修改</p>
<h3>6.7&nbsp;<strong>注意事项</strong></h3>
<p class="15">1、能不能将一个RDD使用广播变量广播出去？</p>
<p class="15">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不能，因为RDD是不存储数据的。<strong>可以将RDD的结果广播出去。</strong></p>
<p class="15">2、&nbsp;广播变量只能在Driver端定义，<strong>不能在Executor端定义。</strong></p>
<p class="15">3、&nbsp;在Driver端可以修改广播变量的值，<strong>在Executor端无法修改广播变量的值。</strong></p>
<p class="15">4、如果executor端用到了Driver的变量，如果<strong>不使用广播变量在Executor有多少task就有多少Driver端的变量副本。</strong></p>
<p class="15">5、如果Executor端用到了Driver的变量，如果<strong>使用广播变量在每个Executor中只有一份Driver端的变量副本。</strong></p>
<h2>七、累加器&nbsp;</h2>
<h3>7.1 累加器的意义</h3>
<p>在spark应用程序中，我们经常会有这样的需求，如异常监控，调试，记录符合某特性的数据的数目，这种需求都需要用到计数器，如果一个变量不被声明为一个累加器，那么它将在被改变时不会再driver端进行全局汇总，即在分布式运行时每个task运行的只是原始变量的一个副本，并不能改变原始变量的值，但是当这个变量被声明为累加器后，该变量就会有分布式计数的功能。</p>
<h3>7.2 图解累加器</h3>
<p>错误的图解</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421164701390-9845184.png" alt="" /></p>
<p>正确的图解</p>
<p><img src="https://images2018.cnblogs.com/blog/1228818/201804/1228818-20180421165419534-240211041.png" alt="" /></p>
<h3>7.3 如何定义一个累加器&nbsp;</h3>
<div class="cnblogs_code">
<pre>val a = sc.accumulator(0)</pre>
</div>
<h3>7.4 如何还原一个累加器&nbsp;</h3>
<div class="cnblogs_code">
<pre>val b = a.value</pre>
</div>
<h3>7.5 累加器的使用</h3>
<div class="cnblogs_code">
<pre>val conf = new<span> SparkConf()
conf.setMaster("local").setAppName("accumulator"<span>)
val sc = new<span> SparkContext(conf)
val accumulator = sc.accumulator(0<span>)
sc.textFile("./words.txt").foreach { x =&gt;{accumulator.add(1<span>)}}
println(accumulator.value)
sc.stop()</span></span></span></span></span></pre>
</div>
<h3>7.6&nbsp;<strong>注意事项</strong></h3>
<p>1、&nbsp;<strong>累加器在Driver端定义赋初始值，累加器只能在Driver端读取最后的值，在Excutor端更新。</strong></p>
<p>2、累加器不是一个调优的操作，因为如果不这样做，结果是错的</p></div>

</body>
</html>
